/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: vfs_msvc.cpp
 *
 * Purpose: virtual file system implementation with windows(visual studio)
 *
 * Developer:
 *   wen.gu , 2019-10-16
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/

#if defined(_MSC_VER)
#include "panda/core/vfs.h"   
#include <Windows.h>
#include <tchar.h>

#define LOG_TAG "vfsw"
#include "panda/core/log.h"

namespace panda
{
namespace core
{
/******************************************************************************
 **    MACROS
 ******************************************************************************/

/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/


struct winFileMode
{
    uint32_t opMode = 0;
    uint32_t dwCreationDisposition = 0;
};

/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/


class VFS::impl
{
public:
    HANDLE mFileHdl = INVALID_HANDLE_VALUE;


public:
    winFileMode getFileOpenMode(OpenMode mode);
    bool setPosition(int64_t offset);
};

winFileMode VFS::impl::getFileOpenMode(OpenMode mode)
{
    winFileMode retMode;
    switch (mode)
    {
    case OpenMode::R:
    {
        retMode.opMode = GENERIC_READ;
        retMode.dwCreationDisposition = OPEN_EXISTING;
        break;
    }    
    case OpenMode::W:
    {
        retMode.opMode = GENERIC_WRITE;
        retMode.dwCreationDisposition = OPEN_EXISTING;
        break;
    }  
    case OpenMode::WCA:
    {
        retMode.opMode = FILE_APPEND_DATA;
        retMode.dwCreationDisposition = OPEN_ALWAYS;
        break;
    }    
    case OpenMode::WCT:
    {
        retMode.opMode = GENERIC_WRITE;
        retMode.dwCreationDisposition = CREATE_ALWAYS; 
        break;
    } 
    case OpenMode::RW:
    {
        retMode.opMode = GENERIC_READ | GENERIC_WRITE;
        retMode.dwCreationDisposition = OPEN_ALWAYS;
        break;
    } 
    case OpenMode::RWA:
    {
        retMode.opMode = GENERIC_READ | GENERIC_WRITE;
        retMode.dwCreationDisposition = OPEN_EXISTING;
        break;
    }
    case OpenMode::RWC:
    {
        retMode.opMode = GENERIC_READ | GENERIC_WRITE;
        retMode.dwCreationDisposition = OPEN_ALWAYS;
        break;
    }
    case OpenMode::RWT:
    {
        retMode.opMode = GENERIC_READ | GENERIC_WRITE;
        retMode.dwCreationDisposition = CREATE_ALWAYS;
        break;
    }
    case OpenMode::RWCA:
    {
        retMode.opMode = FILE_APPEND_DATA | FILE_READ_DATA;
        retMode.dwCreationDisposition = OPEN_ALWAYS;
        break;
    }    
    case OpenMode::RWCT:
    {
        retMode.opMode = GENERIC_WRITE | GENERIC_READ;
        retMode.dwCreationDisposition = CREATE_ALWAYS; 
        break;
    }    
    default:
        break;
    }

    return retMode;
}

bool VFS::impl::setPosition(int64_t offset)
{
    LARGE_INTEGER myOffset = { 0 };
    myOffset.QuadPart = offset;
    myOffset.LowPart = SetFilePointer(mFileHdl, myOffset.LowPart,
        &(myOffset.HighPart), FILE_BEGIN);

    if (myOffset.LowPart != INVALID_SET_FILE_POINTER)
    {
        return ((int64_t)myOffset.QuadPart) == offset;
    }

    return false;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
VFS::VFS()
    :mImpl(new impl)
{
    /** todo something */
}

VFS::~VFS()
{
    /** todo something */
}
 
/*
*@brief open a file
*@param name [in] the name of file(include path).
*@param mode [in] open file with input mode
*@return if sucess return OK, else return error code.
*
**/
PandaErrc VFS::fileOpen(const std::string& fileName, OpenMode mode)
{
    if (mImpl->mFileHdl != INVALID_HANDLE_VALUE)
    {
        fileClose();
    }

    winFileMode wfm = mImpl->getFileOpenMode(mode);

    if (wfm.opMode)
    {
        mImpl->mFileHdl = CreateFile(fileName.c_str(), wfm.opMode, 0, NULL, 
                                     wfm.dwCreationDisposition, 
                                     FILE_ATTRIBUTE_NORMAL, NULL);

        if (mImpl->mFileHdl == INVALID_HANDLE_VALUE)
        {
            LOGE("create file(%s) failed(%d)\n", 
                fileName.c_str(), GetLastError());
            return PandaErrc::InsufficientResources;
        }

        return PandaErrc::OK;
    }

    return PandaErrc::BadParameter;

}

void VFS::fileClose()
{
    if (mImpl->mFileHdl != INVALID_HANDLE_VALUE)
    {
        CloseHandle(mImpl->mFileHdl);
        mImpl->mFileHdl = INVALID_HANDLE_VALUE;
    }
}

/*
*@brief read data from file
*@param offset [in] the offset in file
*@param buf    [io] data buffer
*@param size   [in] data size
*@return if sucess return really read size, else return -1.
*
*@note if read to file end, will be return '0'
*
**/
int32_t VFS::fileRead(int64_t offset, void* buf, uint32_t size)
{
    if (mImpl->mFileHdl != INVALID_HANDLE_VALUE)
    {
        if (mImpl->setPosition(offset))
        {
            uint32_t readSize = 0;
            BOOL readRet = ReadFile(mImpl->mFileHdl, (LPVOID)buf, 
                                    size, (LPDWORD)&readSize, NULL);

            if (readRet == TRUE)
            {
                return readSize;
            }
        }

        //todo get error.
    }

    return -1;
}

/*
*@brief write data to file
*@param offset [in] the offset in file
*@param buf    [io] data buffer
*@param size   [in] data size
*@return if sucess return really write size, else return -1.
*
**/
int32_t VFS::fileWrite(int64_t offset, const void* buf, uint32_t size)
{
    if (mImpl->mFileHdl != INVALID_HANDLE_VALUE)
    {
        if (mImpl->setPosition(offset))
        {
            uint32_t readSize = 0;
            BOOL writeRet = WriteFile(mImpl->mFileHdl, (LPVOID)buf, 
                                      size, (LPDWORD)&readSize, NULL);

            if (writeRet == TRUE)
            {
                return readSize;
            }
        }

        //todo get error.
    }

    return -1;
}

/*
*@brief sync file data
*@return if sucess return OK, else return error code.
*
**/
PandaErrc VFS::fileSync()
{
    if (mImpl->mFileHdl != INVALID_HANDLE_VALUE)
    {
        if (FlushFileBuffers(mImpl->mFileHdl) == TRUE)
        {
            return PandaErrc::OK;
        }
    }

    return PandaErrc::NoInit;
}

/*
*@brief get file size
*@return if sucess return file size, else return '0'.
*
**/
int64_t VFS::fileSize()
{
    if (mImpl->mFileHdl != INVALID_HANDLE_VALUE)
    {
        LARGE_INTEGER size = { 0 };
        size.LowPart = GetFileSize(mImpl->mFileHdl, (LPDWORD)&(size.HighPart));

        if (size.LowPart != INVALID_FILE_SIZE)
        {
            return (int64_t)size.QuadPart;
        }
    }

    return 0;
}

} /** namespace core */
} /** namespace panda */

#endif /** defined _MSC_VER */
